using System;

namespace SharpMud.Net.Telnet.ANSI
{
	public class Utility
	{		
		protected Utility()
		{
		}

        static Utility()
        {
            InitializeColorLookup();
        }

        internal const int ForegroundDefaultColor = 39;
        internal const int BackgroundDefaultColor = 49;
        internal const int BackgroundForegroundDifference = BackgroundDefaultColor - ForegroundDefaultColor;

		/// <summary>
		/// This property returns a string for the regular expression that will match ANSI
		/// control sequences with 99% accuracy.
		/// </summary>
		public static string AnsiSequenceRegex
		{
			get
			{
				return System.Text.RegularExpressions.Regex.Escape(ESC) + @"\[{0,1}\d*[;\d]*[a-zA-Z]";
			}
		}

		/// <summary>
		/// Returns the ANSI escape sequence that will move the cursor UP the
		/// given number of lines
		/// </summary>
		/// <param name="numLines">The number of lines to move the cursor</param>
		/// <returns>Returns an ANSI escape sequence</returns>
		public static string MoveCursorUp(int numLines)
		{
			return ESC + String.Format("[{0}A",numLines);
		}

		/// <summary>
		/// Returns the ANSI escape sequence that will move the cursor DOWN the
		/// given number of lines
		/// </summary>
		/// <param name="numLines">The number of lines to move the cursor</param>
		/// <returns>Returns an ANSI escape sequence</returns>
		public static string MoveCursorDown(int numLines)
		{
			return ESC + String.Format("[{0}B",numLines);
		}

		/// <summary>
		/// Returns the ANSI escape sequence that will move the cursor RIGHT the
		/// given number of columns
		/// </summary>
		/// <param name="numCols">The number of columns to move the cursor</param>
		/// <returns>Returns an ANSI escape sequence</returns>
		public static string MoveCursorRight(int numCols)
		{
			return ESC + String.Format("[{0}C",numCols);
		}

		/// <summary>
		/// Returns the ANSI escape sequence that will move the cursor LEFT the
		/// given number of columns
		/// </summary>
		/// <param name="numCols">The number of columns to move the cursor</param>
		/// <returns>Returns an ANSI escape sequence</returns>
		public static string MoveCursorLeft(int numCols)
		{
			return ESC + String.Format("[{0}D",numCols);
		}

		/// <summary>
		/// Returns the ANSI sequence that instructs the terminal to save the
		/// current cursor position
		/// </summary>
		public static string SaveCursorPosition
		{
			get
			{
				return ESC + "[s";
			}
		}

		/// <summary>
		/// Returns the ANSI sequence that instructs the terminal to resume the
		/// cursor position to the last saved one
		/// </summary>
		public static string LoadCursorPosition
		{
			get
			{
				return ESC + "[u";
			}
		}

		/// <summary>
		/// Returns the ANSI sequence that clears the screen and moves the cursor
		/// to the beginning of the line
		/// </summary>
		public static string ClearScreenAndHomeCursor
		{
			get
			{
				return ESC + "[2J";
			}
		}

		/// <summary>
		/// Returns the ANSI sequence that clears to the end of the current line
		/// </summary>
		public static string ClearToEOL
		{
			get
			{
				return ESC + "K";
			}
		}

        public static string SetColor(ForegroundColor foregroundColor)
        {
            string seq=null;
            bool found=false;
            found = _ForegroundLookupTable.TryGetValue(foregroundColor, out seq);
            if (!found)
                return null;
            if (found)
                return seq;
            return null;
        }

        public static string SetColor(BackgroundColor backgroundColor)
        {
            string seq = null;
            bool found = false;
            found = _BackgroundLookupTable.TryGetValue(backgroundColor, out seq);
            if (!found)
                return null;
            if (found)
                return seq;
            return null;
        }

		public static string SetForegroundColor(BasicColor foregroundColor)
		{
			return ESC + String.Format("[{0}m",
				(int)MakeScopedColor(foregroundColor,TextColorScope.FG) );
		}

		public static string SetBackgroundColor(BasicColor backgroundColor)
		{
			return ESC + String.Format("[{0}m",
				(int)MakeScopedColor(backgroundColor,TextColorScope.BG) );
		}

        public static string SetForegroundColorDefault()
        {
            return ESC + String.Format("[{0},", (int)Utility.ForegroundDefaultColor);
        }

        public static string SetBackgroundColorDefault()
        {
            return ESC + String.Format("[{0},", (int)Utility.BackgroundDefaultColor);
        }

		/// <summary>
		/// Returns an ANSI sequence that operates the given attribute, a 
		/// foreground color, and a background color, on the terminal
		/// </summary>
		/// <param name="attribute">An extended text attribute to use</param>
		/// <param name="foregroundColor">The foreground color to use</param>
		/// <param name="backgroundColor">The background color to use</param>
		/// <returns>Returns an ANSI escape sequence</returns>
        public static string SetTextAttributes(ExtendedTextAttribute attribute, BasicColor foregroundColor, BasicColor backgroundColor)
		{
			return ESC + String.Format("[{0};{1};{2}m",
				(int)attribute,
				(int)MakeScopedColor(foregroundColor,TextColorScope.FG),
				(int)MakeScopedColor(backgroundColor,TextColorScope.BG));
		}

        public static string SetTextColor(BasicColor basicAnsiColor, ColorScope colorScope)
        {
            return SetTextColor(basicAnsiColor, colorScope, false);
        }

        public static string SetTextColor(BasicColor basicAnsiColor, ColorScope colorScope, bool useBright)
        {
            if (colorScope == ColorScope.Background && useBright == true)
            {
                throw new ArgumentException("You cannot combine the 'bright' attribute with a background color.");
            }
            if (!System.Enum.IsDefined(typeof(ColorScope), colorScope))
                throw new ArgumentException("Argument value undefined.", "colorScope");
            if (!System.Enum.IsDefined(typeof(BasicColor), basicAnsiColor))
                throw new ArgumentException("Argument value undefined.", "basicAnsiColor");

            int resultAnsiColor;
            if (colorScope == ColorScope.Foreground)
                resultAnsiColor = (int)basicAnsiColor;
            else
                resultAnsiColor = (int)basicAnsiColor + Utility.BackgroundForegroundDifference;

            if (!useBright)
                return ESC + String.Format("[{0};{1}m", (int)ExtendedTextAttribute.Normal,resultAnsiColor);
            if (useBright)
                return ESC + String.Format("[{0};{1}m", (int)ExtendedTextAttribute.Bold, resultAnsiColor);

            return null;
        }

		/// <summary>
		/// Returns an ANSI sequence that operates the given ANSI attribute on a terminal
		/// </summary>
		/// <param name="attribute">An extended text attribute to use</param>
		/// <returns>Returns an ANSI escape sequence</returns>
		public static string SetTextAttributes(ExtendedTextAttribute attribute)
		{
			return ESC + String.Format("[{0}m",
				(int)attribute );
		}

		/// <summary>
		/// Returns an ANSI sequence that operates the given
		/// foreground color and background color on the terminal
		/// </summary>
		/// <param name="foregroundColor">The foreground color to use</param>
		/// <param name="backgroundColor">The background color to use</param>
		/// <returns>Returns an ANSI escape sequence</returns>
		public static string SetTextAttributes(BasicColor foregroundColor, BasicColor backgroundColor)
		{
			return ESC + String.Format("[{0};{1}m",
				(int)MakeScopedColor(foregroundColor,TextColorScope.FG),
				(int)MakeScopedColor(backgroundColor,TextColorScope.BG));
		}

		/// <summary>
		/// Returns the ANSI escape sequence that will move the cursor to another
		/// onscreen location
		/// </summary>
		/// <param name="line">The line number to move the cursor to</param>
		/// <param name="col">The column number to move the cursor to</param>
		/// <returns>Returns an ANSI escape sequence</returns>
		public static string MoveCursorTo(int line, int col)
		{
			return ESC + String.Format("[{0};{1}H",line,col);
		}

		
		/// <summary>
		/// The magic string (character) that must begin all ANSI control sequences
		/// </summary>
		public const string ESC = "\x1B"; // ASCII # 33

		/// <summary>
		/// Whether or not a color is for the foreground or background
		/// </summary>
		internal enum TextColorScope
		{
			Foreground = ColorScope.Foreground,
			Background = ColorScope.Background,
			FG = Foreground,
			BG = Background
		}

		/// <summary>
		/// This method takes a basic color and translates it to the form that can be inserted
		/// into an ANSI sequence, by taking into account whether or not it is a foreground or
		/// background color.
		/// </summary>
		/// <param name="color">The basic color to translate</param>
		/// <param name="scope">Whether or not the color is for foreground or background</param>
		/// <returns>Returns a scoped color</returns>
		internal static ScopedColor MakeScopedColor(BasicColor color, TextColorScope scope)
		{
			if(scope==TextColorScope.Foreground)
			{
				return (ScopedColor)color;
			}
			else if(scope==TextColorScope.Background)
			{
				return (ScopedColor)(color+10);
			}
			else
			{
				throw new ArgumentOutOfRangeException("scope");
			}
        }

        #region Color Value Lookup
        private static System.Collections.Generic.Dictionary<ForegroundColor, string> _ForegroundLookupTable;
        private static System.Collections.Generic.Dictionary<BackgroundColor, string> _BackgroundLookupTable;

        /// <summary>
        /// Since ANSI sequences for enum-based color values are stored using custom attributes,
        /// and reflection isn't the fastest way to lookup data, we need to pre-fetch the data
        /// </summary>
        private static void InitializeColorLookup()
        {
            _ForegroundLookupTable = new System.Collections.Generic.Dictionary<ForegroundColor, string>();
            _BackgroundLookupTable = new System.Collections.Generic.Dictionary<BackgroundColor, string>();

            string s = null;
            AnsiRepresentationAttribute ara = null;

            ///Program in ForegroundColor lookups
            foreach (ForegroundColor colorValue in System.Enum.GetValues(typeof(ForegroundColor)))
            {
                s = null;
                ara = null;

                ara = AnsiRepresentationAttribute.FindForValue<ForegroundColor>(colorValue);
                if (ara != null)
                {
                    if (_ForegroundLookupTable.ContainsKey(colorValue) == false)
                    {
                        s = ara.Sequence;
                        _ForegroundLookupTable.Add(colorValue, s);
                    }
                }
            }

            ///Program in BackgroundColor lookups
            foreach (BackgroundColor colorValue in System.Enum.GetValues(typeof(BackgroundColor)))
            {
                s = null;
                ara = null;

                ara = AnsiRepresentationAttribute.FindForValue<BackgroundColor>(colorValue);
                if (ara != null)
                {
                    if (_BackgroundLookupTable.ContainsKey(colorValue) == false)
                    {
                        s = ara.Sequence;
                        _BackgroundLookupTable.Add(colorValue, s);
                    }
                }
            }
        }
        
        #endregion
    }
}
